package it.unibz.inf.ontop.protege.panels;

/*
 * #%L
 * ontop-protege4
 * %%
 * Copyright (C) 2009 - 2013 KRDB Research Centre. Free University of Bozen Bolzano.
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import it.unibz.inf.ontop.protege.core.OBDADataSource;
import it.unibz.inf.ontop.protege.gui.IconLoader;
import it.unibz.inf.ontop.protege.gui.treemodels.ResultSetTableModel;
import it.unibz.inf.ontop.protege.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.*;
import javax.swing.table.TableModel;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.CountDownLatch;

public class SQLQueryPanel extends javax.swing.JPanel implements DatasourceSelectorListener {

	private static final long serialVersionUID = 7600557919206933923L;

	private Logger log = LoggerFactory.getLogger(SQLQueryPanel.class);

	private OBDADataSource selectedSource;

	/**
	 * Creates new form SQLQueryPanel
	 */
	public SQLQueryPanel() {
		initComponents();
	}

	public SQLQueryPanel(OBDADataSource ds, String query) {
		initComponents();
		txtSqlQuery.setText(query);
		selectedSource = ds;
		cmdExecuteActionPerformed(null);
	}

	/**
	 * This method is called from within the constructor to initialize the form.
	 * WARNING: Do NOT modify this code. The content of this method is always
	 * regenerated by the Form Editor.
	 */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        splSqlQuery = new javax.swing.JSplitPane();
        pnlSqlQuery = new javax.swing.JPanel();
        scrSqlQuery = new javax.swing.JScrollPane();
        txtSqlQuery = new javax.swing.JTextArea();
        cmdExecute = new javax.swing.JButton();
        pnlQueryResult = new javax.swing.JPanel();
        scrQueryResult = new javax.swing.JScrollPane();
        tblQueryResult = new javax.swing.JTable();

        setBorder(javax.swing.BorderFactory.createTitledBorder("SQL Query"));
        setAlignmentX(5.0F);
        setAlignmentY(5.0F);
        setFont(new java.awt.Font("Arial", 0, 18)); // NOI18N
        setPreferredSize(new java.awt.Dimension(640, 480));
        setLayout(new java.awt.BorderLayout(5, 5));

        splSqlQuery.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
        splSqlQuery.setResizeWeight(0.3);

        pnlSqlQuery.setMinimumSize(new java.awt.Dimension(156, 100));
        pnlSqlQuery.setPreferredSize(new java.awt.Dimension(156, 100));
        pnlSqlQuery.setLayout(new java.awt.GridBagLayout());

        scrSqlQuery.setPreferredSize(new java.awt.Dimension(600, 100));

        txtSqlQuery.setColumns(20);
        txtSqlQuery.setFont(new java.awt.Font("Dialog", 0, 12)); // NOI18N
        txtSqlQuery.setLineWrap(true);
        txtSqlQuery.setRows(2);
        txtSqlQuery.setBorder(null);
        txtSqlQuery.setPreferredSize(new java.awt.Dimension(600, 100));
        scrSqlQuery.setViewportView(txtSqlQuery);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.gridwidth = 3;
        gridBagConstraints.gridheight = 2;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
        gridBagConstraints.weightx = 2.0;
        gridBagConstraints.weighty = 2.0;
        pnlSqlQuery.add(scrSqlQuery, gridBagConstraints);

        cmdExecute.setIcon(IconLoader.getImageIcon("images/execute.png"));
        cmdExecute.setMnemonic('x');
        cmdExecute.setText("Execute");
        cmdExecute.setToolTipText("Execute the SQL query");
        cmdExecute.setBorder(javax.swing.BorderFactory.createEtchedBorder());
        cmdExecute.setContentAreaFilled(false);
        cmdExecute.setMargin(new java.awt.Insets(5, 14, 5, 14));
        cmdExecute.setMaximumSize(new java.awt.Dimension(85, 25));
        cmdExecute.setMinimumSize(new java.awt.Dimension(85, 25));
        cmdExecute.setPreferredSize(new java.awt.Dimension(85, 25));
        cmdExecute.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                cmdExecuteActionPerformed(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 2;
        gridBagConstraints.gridwidth = java.awt.GridBagConstraints.RELATIVE;
        gridBagConstraints.gridheight = java.awt.GridBagConstraints.RELATIVE;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
        pnlSqlQuery.add(cmdExecute, gridBagConstraints);

        splSqlQuery.setLeftComponent(pnlSqlQuery);

        pnlQueryResult.setLayout(new java.awt.BorderLayout());

        tblQueryResult.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "Results"
            }
        ));
        tblQueryResult.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF);
        scrQueryResult.setViewportView(tblQueryResult);

        pnlQueryResult.add(scrQueryResult, java.awt.BorderLayout.CENTER);

        splSqlQuery.setRightComponent(pnlQueryResult);

        add(splSqlQuery, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

	private void cmdExecuteActionPerformed(java.awt.event.ActionEvent evt) {
		releaseResultset();
		try {
			if (selectedSource == null) {
				JOptionPane.showMessageDialog(this, "Please select data source first", "Error", JOptionPane.ERROR_MESSAGE);
			} else {
				OBDAProgressMonitor progMonitor = new OBDAProgressMonitor("Executing query...", getRootPane());
				CountDownLatch latch = new CountDownLatch(1);
				ExecuteSQLQueryAction action = new ExecuteSQLQueryAction(latch);
				progMonitor.addProgressListener(action);
				progMonitor.start();
				action.run();
				latch.await();
				progMonitor.stop();
				ResultSet set = action.getResult();
				if (set != null) {
					ResultSetTableModel model = new ResultSetTableModel(set);
					tblQueryResult.setModel(model);
					set.close();
				}
			}
		} catch (Exception e) {
			OptionPaneUtils.showPrettyMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
			log.error("Error while executing query.", e);
		}
	}

	private void releaseResultset() {
		TableModel model = tblQueryResult.getModel();
		if (model == null) {
			return;
		}
		if (!(model instanceof ResultSetTableModel)) {
			return;
		}
		ResultSetTableModel imodel = (ResultSetTableModel) model;
		imodel.close();
	}

	@Override
	public void datasourceChanged(OBDADataSource oldSource, OBDADataSource newSource) {
		this.selectedSource = newSource;
		releaseResultset();
	}

	@Override
	public void finalize() {
		releaseResultset();
	}

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cmdExecute;
    private javax.swing.JPanel pnlQueryResult;
    private javax.swing.JPanel pnlSqlQuery;
    private javax.swing.JScrollPane scrQueryResult;
    private javax.swing.JScrollPane scrSqlQuery;
    private javax.swing.JSplitPane splSqlQuery;
    private javax.swing.JTable tblQueryResult;
    private javax.swing.JTextArea txtSqlQuery;
    // End of variables declaration//GEN-END:variables

	private class ExecuteSQLQueryAction implements OBDAProgressListener {

		CountDownLatch latch = null;
		Thread thread = null;
		ResultSet result = null;
		Statement statement = null;
		private boolean isCancelled = false;
		private boolean errorShown = false;

		private ExecuteSQLQueryAction(CountDownLatch latch) {
			this.latch = latch;
		}

		@Override
		public void actionCanceled() throws SQLException {
			this.isCancelled = true;
			if (thread != null) {
				thread.interrupt();
			}
			if (statement != null && !statement.isClosed()) {
				statement.close();
			}
			result = null;
			latch.countDown();
		}

		public ResultSet getResult() {
			return result;
		}

		public void run() {
			thread = new Thread() {
				public void run() {
					try {
						TableModel oldmodel = tblQueryResult.getModel();

						if ((oldmodel != null) && (oldmodel instanceof ResultSetTableModel)) {
							ResultSetTableModel rstm = (ResultSetTableModel) oldmodel;
							rstm.close();
						}
						Connection c = ConnectionTools.getConnection(selectedSource);
						Statement s = c.createStatement();
						s.setMaxRows(100);
						result = s.executeQuery(txtSqlQuery.getText());
						latch.countDown();
					} catch (Exception e) {
						latch.countDown();
						errorShown = true;

						OptionPaneUtils.showPrettyMessageDialog(null, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);

						log.error("Error while executing query.", e);
					}
				}
			};
			thread.start();
		}

		@Override
		public boolean isCancelled() {
			return this.isCancelled;
		}

		@Override
		public boolean isErrorShown() {
			return this.errorShown;
		}

	}
}
